"
I keep a  registry of FFI prepared methods. 
Registry is used to know exactly which methods are executing FFI-NB calls and then which ones need to be recompiled when a platform has change.
"
Class {
	#name : 'FFIMethodRegistry',
	#superclass : 'Object',
	#instVars : [
		'compiledMethods'
	],
	#classInstVars : [
		'uniqueInstance'
	],
	#category : 'UnifiedFFI-Base',
	#package : 'UnifiedFFI',
	#tag : 'Base'
}

{ #category : 'cleanup' }
FFIMethodRegistry class >> cleanUp [
	"Only delete change sets when being aggressive"

	self uniqueInstance reset.
	uniqueInstance := nil
]

{ #category : 'class initialization' }
FFIMethodRegistry class >> initialize [
	SessionManager default
		registerSystemClassNamed: self name
		atPriority: 60
]

{ #category : 'instance creation' }
FFIMethodRegistry class >> new [
	self error: 'Use #uniqueInstance'
]

{ #category : 'system startup' }
FFIMethodRegistry class >> resetAll [
	"I'm reseting each shutdown and startup because like that I can be sure a complete cleanup is done,
	 because many things can change:
		- platform change
		- working directory change (and then libraries not found)
		- indirect functions needs to be recalculated.
	I am not supposed to be performed when image is just saved to avoid possible problem which can happen when we reset all caches and continue to work"

	FFICallbackFunctionResolution reset.
	FFIStructure resetAllStructures.
	self cleanUp
]

{ #category : 'system startup' }
FFIMethodRegistry class >> softResetAll [
	"I'm reseting each shutdown and startup because like that I can be sure a complete cleanup is done,
	 because many things can change:
		- platform change
		- working directory change (and then libraries not found)
		- indirect functions needs to be recalculated.
	I am not supposed to be performed when image is just saved to avoid possible problem which can happen when we reset all caches and continue to work
	
	The soft version is not reseting structures if the platform did not change."

	FFICallbackFunctionResolution reset.
	FFIStructure resetAllStructuresIfPlatformChanged.
	self cleanUp
]

{ #category : 'system startup' }
FFIMethodRegistry class >> startUp: isImageStarting [
	isImageStarting ifFalse: [ ^ self ].

	self softResetAll
]

{ #category : 'instance creation' }
FFIMethodRegistry class >> uniqueInstance [
	^ uniqueInstance ifNil: [ uniqueInstance := super new ]
]

{ #category : 'accessing' }
FFIMethodRegistry >> compiledMethods [
	^ compiledMethods
]

{ #category : 'initialization' }
FFIMethodRegistry >> initialize [
	super initialize.
	compiledMethods := WeakSet new
]

{ #category : 'accessing' }
FFIMethodRegistry >> registerMethod: aCompiledMethod [
	compiledMethods add: aCompiledMethod
]

{ #category : 'initialization' }
FFIMethodRegistry >> reset [
	"FFI compiled methods will keep the old method as a property, making easy to replace it
	 when changing platforms."
	self compiledMethods do: [ :each | 
		self resetMethod: each ].
	self compiledMethods removeAll
]

{ #category : 'accessing' }
FFIMethodRegistry >> resetMethod: ffiMethod [

	"Only restore methods that are still installed.
	Otherwise, it may happen that the method has been replaced 
	and thus not yet GC'd and we will restore a wrong method."

	| originalMethod |
	
	ffiMethod isInstalled ifFalse: [ ^ self ].
	
	originalMethod := ffiMethod propertyAt: #ffiNonCompiledMethod.
	ffiMethod methodClass methodDict
		at: ffiMethod selector
		put: originalMethod
]

{ #category : 'reseting' }
FFIMethodRegistry >> resetSingleClass: aClass [

	self compiledMethods
		select: [ :m | m methodClass = aClass ]
		thenDo: [ :aMethod |
			self resetMethod: aMethod.
			compiledMethods remove: aMethod ]
]
